package org.xtext.runtime.teleoreactive;

import java.util.List;

import org.xtext.runtime.formula.Predicate;
import org.xtext.runtime.interpreter.Agent;
import org.xtext.runtime.util.Bindings;

public class TRFunction {
	private Predicate identifier;
	private TRRule[] rules;
	private int selected = -1;
	private ITRAction action = null;
	private Bindings binding;
	
	public TRFunction(Predicate identifier, TRRule[] rules) {
		this.identifier = identifier;
		this.rules = rules;
	}
	
	public TRRule[] rules() {
		return rules;
	}

	public Predicate identifier() {
		return identifier;
	}
	
	public boolean invoke(Agent agent) {
		for (int i=0; i<rules.length; i++) {
			List<Bindings> bindings = agent.query(rules[i].condition());
//			System.out.println("bindings: " + bindings);
			if (bindings != null) {
				if (i == selected) {
//					System.out.println("[" + this.identifier + "] This is the same as the previous rule");
					if (bindings.isEmpty() && Bindings.EMPTY_BINDINGS.equals(binding)) {
						action.handle(agent);
						return true;
					} else {
						for (Bindings b : bindings) {
							if (b.equals(binding)) {
//								System.out.println("[" + this.identifier + "] Found match...");
								action.handle(agent);
								return true;
							}
						}
						
//						System.out.println("[" + this.identifier + "] Change of rule...");
						action = rules[i].action().apply(bindings.get(0));
						action.handle(agent);
						return true;
					}
				} else {
					selected = i;
					if (bindings.isEmpty()) {
						binding = Bindings.EMPTY_BINDINGS;
					} else {
						binding = bindings.get(0);
					}
					
					action = rules[i].action().apply(binding);
					action.handle(agent);
					return true;
				}
			}
		}
		selected = -1;
		return false;
	}

	public TRFunction apply(Bindings bindings) {
		TRRule[] r = new TRRule[rules.length];
		for (int i=0; i < rules.length; i++) {
			r[i] = rules[i].apply(bindings);
		}
		return new TRFunction(identifier.copy().apply(bindings), r);
	}

	public TRFunction copy() {
		TRRule[] r = new TRRule[rules.length];
		for (int i=0; i < rules.length; i++) {
			r[i] = rules[i].copy();
		}
		return new TRFunction(identifier.copy(), r);
	}
	
	public String toString() {
		return identifier + ((selected >= 0) ? "->" + rules[selected].action() : "");
	}
	
	public boolean equals(Object obj) {
		if (TRFunction.class.isInstance(obj)) {
			TRFunction fn = (TRFunction) obj;
			return identifier.equals(fn.identifier);
		}
		return false;
	}
}
